package com.tony.threadExecutor;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.stream.IntStream;

/**
 * @author tonywang
 * @date 8/2/2018
 */
public class FactorialTask extends RecursiveTask<BigInteger> {
    private int start = 1;
    private int n;
    private static final int THRESHOLD = 20;

    // standard constructors
    public FactorialTask(int start, int n) {
        this.start = start;
        this.n = n;
    }

    public FactorialTask(int n) {
        this.n = n;
    }

    public static void main(String[] args){
//        ForkJoinPool pool = ForkJoinPool.commonPool();
//        BigInteger result = pool.invoke(new FactorialTask(4));
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
//        Optional<Integer> result1 = list.parallelStream().reduce((a, b)->a*(b) + 2);
//        Optional<Integer> result2 = list.stream().reduce((a, b)->a*(b) + 2);
        Integer result3 = list.parallelStream().reduce(1,(a, b)->a*b+2,(a,b)->a*b+2);
//        Integer result4 = list.stream().reduce(1,(a, b)->a*b + 2,(a,b)->a*b);

        System.out.println(result3);
    }


    @Override
    protected BigInteger compute() {
        if ((n - start) >= THRESHOLD) {
            return ForkJoinTask.invokeAll(createSubtasks())
                    .stream()
                    .map(ForkJoinTask::join)
                    .reduce(BigInteger.ONE, BigInteger::multiply);
        } else {
            return calculate(start, n);
        }
    }

    private Collection<FactorialTask> createSubtasks() {
        List<FactorialTask> dividedTasks = new ArrayList<>();
        int mid = (start + n) / 2;
        dividedTasks.add(new FactorialTask(start, mid));
        dividedTasks.add(new FactorialTask(mid + 1, n));
        return dividedTasks;
    }

    private BigInteger calculate(int start, int n) {
        return IntStream.rangeClosed(start, n)
                .mapToObj(BigInteger::valueOf)
                .reduce(BigInteger.valueOf(2), BigInteger::multiply);
    }
}
